#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define MATRIX_SIZ  4

enum direct {
	LEFT = 1,
	UP,
	RIGHT,
	DOWN
} dir;


int matrix[MATRIX_SIZ][MATRIX_SIZ];
int insert_x, insert_y;

/*
 * 显示当前内容
 */
void show()
{
	int i, j;

	// 清除屏幕
	printf("\033[2J");

	printf(
    "   ||||      ||         ||     |||    \n"
    "  ||||||    || ||       ||    || ||   \n"
    "  |   ||    |   |      |||   ||   ||  \n"
    "  |   ||   ||   |     | ||   ||   ||  \n"
    "      ||   ||   ||   || ||   ||||||   \n"
    "      |    ||   ||   |  ||    |||||   \n"
    "     |     ||   ||  |||||||   |  |||  \n"
    "    |      ||   |   |||||||  ||   ||  \n"
    "   |    |  ||   |       ||   ||   ||  \n"
    "  |||||||   |  ||       ||   ||   ||  \n"
    " ||||||||    |||        ||    |||||   \n"
	);

	for (i=0; i<MATRIX_SIZ; i++) {
		for (j=0; j<MATRIX_SIZ; j++) {
			// 插入位置闪烁
			if (i == insert_x && j == insert_y)
				printf("\033[5m");

			if (matrix[i][j] == 0)
				printf("      ");
			else
				printf(" %4d ", matrix[i][j]);
			// 其他位置不闪
			if (i == insert_x && j == insert_y)
				printf("\033[0m");
		}
		printf("\n");
		for (j=0; j<MATRIX_SIZ; j++) {
			 printf("------|");
		}
		printf("\n");
	}
}

/*
 * 获取移动方向
 */
int get_direct(void)
{
	int ch;
	enum direct dir;
	printf("-----------------------=----------------------------\n"
		   "Input h(LEFT), k(UP), j(DOWN), l(RIGHT)\n\n");
	switch (getchar()) {
	case 'h':
		dir = LEFT;
		break;
	case 'k':
		dir = UP;
		break;
	case 'j':
		dir = DOWN;
		break;
	case 'l':
		dir = RIGHT;
		break;
	default:
		break;
	}
	// 读直到换行
	while ((ch = getchar()) != 0xa) {
		putchar(ch);
	}

	return dir;
}

/*
 *	在反方向插入一个数
 */
int insert(enum direct dir)
{
	int i, j;

	switch (dir) {
		case LEFT:
			for (i=0; i<MATRIX_SIZ; i++) {
				for (j=MATRIX_SIZ-1; j>=0; j--) {
					if (matrix[i][j] == 0) {
						matrix[i][j]= 2;
						insert_x = i;
						insert_y = j;
						return 1;
					}
				}
			}
			break;
		case DOWN:
			for (j=0; j<MATRIX_SIZ; j++) {
				for (i=0; i<MATRIX_SIZ; i++) {
					if (matrix[i][j] == 0) {
						matrix[i][j]= 2;
						insert_x = i;
						insert_y = j;
						return 1;
					}
				}
			}
			break;
		case UP:
			for (j=0; j<MATRIX_SIZ; j++) {
				for (i=MATRIX_SIZ-1; i>=0; i--) {
					if (matrix[i][j] == 0) {
						matrix[i][j]= 2;
						insert_x = i;
						insert_y = j;
						return 1;
					}
				}
			}
			break;
		case RIGHT:
			for (i=0; i<MATRIX_SIZ; i++) {
				for (j=0; j<MATRIX_SIZ; j++) {
					if (matrix[i][j] == 0) {
						matrix[i][j]= 2;
						insert_x = i;
						insert_y = j;
						return 1;
					}
				}
			}
			break;
	}

	return 0;
}

int add(int arr[], int n)
{
	int i, size;
	
	size = n;
	for(i=0;i<size;){
		if (size == 1) break;
		if (arr[i] == 0) {
			if (i < size - 1)
				memmove(&arr[i], &arr[i+1], (size-i-1)*sizeof(int));
			size -= 1;
			if (i != 0)
				i = 0;
		}
		else if (i < size - 1 && arr[i] == arr[i+1]) {
			arr[i] = arr[i] + arr[i+1];
			memmove(&arr[i+1], &arr[i+2], (size-i-1)*sizeof(int));
			size -= 1;
			if (i != 0)
				i = 0;
		}
		else {
			i++;
		}
	}
	for (; size<n; size++)
		arr[size] = 0;

	return n;
}

/*
 * 根据移动方向进行合并
 */
void move(enum direct dir)
{
	int i, j, vector[MATRIX_SIZ];

	switch (dir) {
		case LEFT:
			for (i=0; i<MATRIX_SIZ; i++) {
				memcpy(vector, matrix[i], MATRIX_SIZ * sizeof(int));
				add(vector, MATRIX_SIZ);
				memcpy(matrix[i], vector, MATRIX_SIZ * sizeof(int));
			}
			break;
		case UP:
			for (j=0; j<MATRIX_SIZ; j++) {
				for (i=0; i<MATRIX_SIZ; i++) {
					vector[i] = matrix[i][j];
				}
				add(vector, MATRIX_SIZ);
				for (i=0; i<MATRIX_SIZ; i++) {
					matrix[i][j] = vector[i] ;
				}
			}
			break;
		case DOWN:
			for (j=0; j<MATRIX_SIZ; j++) {
				for (i=0; i<MATRIX_SIZ; i++) {
					vector[i] = matrix[MATRIX_SIZ-1-i][j];
				}
				add(vector, MATRIX_SIZ);
				for (i=0; i<MATRIX_SIZ; i++) {
					matrix[i][j] = vector[MATRIX_SIZ-1-i];
				}
			}
			break;
		case RIGHT:
			for (i=0; i<MATRIX_SIZ; i++) {
				for (j=0; j<MATRIX_SIZ; j++) {
					vector[j] = matrix[i][MATRIX_SIZ-1-j];
				}
				add(vector, MATRIX_SIZ);
				for (j=0; j<MATRIX_SIZ; j++) {
					matrix[i][j] = vector[MATRIX_SIZ-1-j];
				}
			}
			break;
	}
}

int main(int argc, char *argv[])
{
	dir = LEFT;

	show();
	do {
		if (insert(dir) == 0) {
			break;
		}
		show();

		dir = get_direct();
		move(dir);
	}
	while (dir != 0);

	printf("Bye\n");

	return 0;
}
